﻿using System;
using System.Diagnostics.Contracts;

namespace ReactiveLang
{
    [ContractClass(typeof(IExpressionObserverContract<>))]
    public interface IExpressionObserver<in T>
    {
        void OnValue(T value);
        void OnException(Exception ex);
    }

    #region Contract for IExpressionObserver<T>

    [ContractClassFor(typeof(IExpressionObserver<>))]
    abstract class IExpressionObserverContract<T> : IExpressionObserver<T>
    {
        public void OnException(Exception ex)
        {
            Contract.Requires<ArgumentNullException>(ex != null, "ex");
        }

        public void OnValue(T value)
        {
        }
    }

    #endregion

    public class ExpressionObserver
    {
        class AnonymousExpressionObserver<T> : IExpressionObserver<T>
        {
            internal Action<T> _onValue;
            public void OnValue(T value)
            {
                _onValue(value);
            }

            internal Action<Exception> _onException;
            public void OnException(Exception ex)
            {
                _onException(ex);
            }
        }
        public static IExpressionObserver<T> Create<T>(Action<T> onValue, Action<Exception> onException = null)
        {
            Contract.Requires<ArgumentNullException>(onValue != null, "value");
            Contract.Ensures(Contract.Result<IExpressionObserver<T>>() != null);
            if (onException == null)
                onException = ex => { throw ex; };
            return new AnonymousExpressionObserver<T> { _onValue = onValue, _onException = onException };
        }
    }
}